using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using usm.SolDev.DataBase.DataObjects;
using System.Text.RegularExpressions;

namespace usm.SolDev.UI
{
    /// <summary>
    ///     Code explorer is a tool window which scans source file for
    /// code elements and show them in a tree
    /// </summary>
    public partial class CodeExplorer : usm.SolDev.UI.ToolWindow
    {
        #region Private members
        List<String> sourceLines = new List<String>();
        List<CodeElement> codeElements = new List<CodeElement>();
        private ProjectDescriptor project;
        private TreeNode mainNode = null;
        
        #endregion

        #region Constructors
        /// <summary>
        ///     Default constructor. Create an initialize global node
        /// </summary>
        public CodeExplorer()
        {
            InitializeComponent();

            mainNode = codeTree.Nodes.Add("Globals");
            mainNode.ImageIndex = (int)CodeElementType.program;
            mainNode.SelectedImageIndex = (int)CodeElementType.program;
        }
        #endregion

        #region Interface functions
        /// <summary>
        ///     Reload project to code explorer. Clear the content and
        /// replace whole source file.
        /// </summary>
        /// <param name="project">the project descriptor</param>
        public void ReloadProject (ProjectDescriptor project)
        {
            this.project = project;
            mainNode.Nodes.Clear();
            codeTree.Nodes.Clear();
            codeTree.Nodes.Add(mainNode);
            

            if (project != null && project.Program != null && project.Program.Length >0)
            {
                TabText = String.Format("Code Explorer - {0}", project.Description);
                ParseSourceCode(project.Program);
            }
            else
            {
                TabText = "Code Explorer";
            }
        }
        #endregion

        #region Helper methods
        /// <summary>
        ///     Decompose source line in included elements
        /// </summary>
        /// <param name="sourceLine">the source line text</param>
        /// <returns>the elemts from this line</returns>
        private List<CodeElement>  GetLineElements (string sourceLine, int lineNumber)
        {
            #region Declarations
            Match matchResult = null;
            List<CodeElement> codeElements = new List<CodeElement>();
            #endregion

            #region processes
            matchResult = Regex.Match(sourceLine, @"(?i)\s*process\s+(?<process_name>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult !=null) && (i < matchResult.Length); i++)
                {
                    CodeElement codeElement = new CodeElement();
                    codeElement.Name = matchResult.Result("${process_name}");
                    codeElement.LineText = sourceLine;
                    codeElement.SourceLine = lineNumber;
                    codeElement.Type = CodeElementType.process;
                    codeElement.Index = matchResult.Index;
                    codeElement.ElementColor = Color.Red;
                    codeElements.Add(codeElement);
                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion

            #region facility
            matchResult = Regex.Match(sourceLine, @"(?i)\s*facility\s+(?<facility_name>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult != null) && (i < matchResult.Length); i++)
                {
                    string[] facilities = matchResult.Result("${facility_name}").Split(new char[] { ',' });
                    if (facilities == null || facilities.Length == 0)
                    {
                        facilities = new string[] { matchResult.Result("${facility_name}").Trim()};
                    }

                    foreach(string facility in facilities)
                    {
                        CodeElement codeElement = new CodeElement();
                        codeElement.Name = facility.Trim();
                        codeElement.LineText = sourceLine;
                        codeElement.SourceLine = lineNumber;
                        codeElement.Type = CodeElementType.facility;
                        codeElement.Index = matchResult.Index; 
                        codeElement.ElementColor = Color.Blue;
                        codeElements.Add(codeElement);
                    }

                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion

            #region monitor
            matchResult = Regex.Match(sourceLine, @"(?i)\s*monitor\s+(?<monitor_name>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult != null) && (i < matchResult.Length); i++)
                {
                    string[] monitors = matchResult.Result("${monitor_name}").Split(new char[] { ',' });
                    if (monitors == null || monitors.Length == 0)
                    {
                        monitors = new string[] { matchResult.Result("${monitor_name}").Trim() };
                    }

                    foreach (string monitor in monitors)
                    {
                        CodeElement codeElement = new CodeElement();
                        codeElement.Name = monitor.Trim();
                        codeElement.LineText = sourceLine;
                        codeElement.SourceLine = lineNumber;
                        codeElement.Type = CodeElementType.monitor;
                        codeElement.Index = matchResult.Index;
                        codeElement.ElementColor = Color.Green;
                        codeElements.Add(codeElement);
                    }

                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion
            
            #region Store
            matchResult = Regex.Match(sourceLine, @"(?i)\s*store\s+(?<store_name>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult != null) && (i < matchResult.Length); i++)
                {
                    string[] stores = matchResult.Result("${store_name}").Split(new char[] { ',' });
                    if (stores == null || stores.Length == 0)
                    {
                        stores = new string[] { matchResult.Result("${store_name}").Trim() };
                    }

                    foreach (string store in stores)
                    {
                        CodeElement codeElement = new CodeElement();
                        codeElement.Name = store.Trim();
                        codeElement.LineText = sourceLine;
                        codeElement.SourceLine = lineNumber;
                        codeElement.Type = CodeElementType.store;
                        codeElement.Index = matchResult.Index;
                        codeElement.ElementColor = Color.Violet;
                        codeElements.Add(codeElement);
                    }

                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion

            #region integer
            matchResult = Regex.Match(sourceLine, @"(?i)\s*integer\s+(?<integer_variable>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult != null) && (i < matchResult.Length); i++)
                {
                    string[] integers = matchResult.Result("${integer_variable}").Split(new char[] { ',' });
                    if (integers == null || integers.Length == 0)
                    {
                        integers = new string[] { matchResult.Result("${integer_variable}").Trim() };
                    }

                    foreach (string integer in integers)
                    {
                        CodeElement codeElement = new CodeElement();
                        codeElement.Name = integer.Trim();
                        codeElement.LineText = sourceLine;
                        codeElement.SourceLine = lineNumber;
                        codeElement.Type = CodeElementType.integer;
                        codeElement.Index = matchResult.Index;
                        codeElement.ElementColor = Color.Tomato;
                        codeElements.Add(codeElement);
                    }

                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion

            #region real
            matchResult = Regex.Match(sourceLine, @"(?i)\s*real\s+(?<real_variable>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult != null) && (i < matchResult.Length); i++)
                {
                    string[] reals = matchResult.Result("${real_variable}").Split(new char[] { ',' });
                    if (reals == null || reals.Length == 0)
                    {
                        reals = new string[] { matchResult.Result("${real_variable}").Trim() };
                    }

                    foreach (string real in reals)
                    {
                        CodeElement codeElement = new CodeElement();
                        codeElement.Name = real.Trim();
                        codeElement.LineText = sourceLine;
                        codeElement.SourceLine = lineNumber;
                        codeElement.Type = CodeElementType.real;
                        codeElement.Index = matchResult.Index;
                        codeElement.ElementColor = Color.Cyan;
                        codeElements.Add(codeElement);
                    }

                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion
            
            #region Table
            matchResult = Regex.Match(sourceLine, @"(?i)\s*table\s+(?<table_variable>[^;]+)");
            if (matchResult != null && matchResult.Length > 0)
            {
                for (int i = 0; (matchResult != null) && (i < matchResult.Length); i++)
                {
                    string[] tables = matchResult.Result("${table_variable}").Split(new char[] { ',' });
                    if (tables == null || tables.Length == 0)
                    {
                        tables = new string[] { matchResult.Result("${table_variable}").Trim() };
                    }

                    foreach (string table in tables)
                    {
                        CodeElement codeElement = new CodeElement();
                        codeElement.Name = table.Trim();
                        codeElement.LineText = sourceLine;
                        codeElement.SourceLine = lineNumber;
                        codeElement.Type = CodeElementType.table;
                        codeElement.Index = matchResult.Index;
                        codeElement.ElementColor = Color.SandyBrown;
                        codeElements.Add(codeElement);
                    }

                    matchResult = matchResult.NextMatch();
                }
            }
            #endregion

            #region Return result
            return codeElements;
            #endregion
        }

        /// <summary>
        ///     Parse source code. Decompose source in lines. Decompose each
        /// line to elements.
        /// </summary>
        /// <param name="SourceCode">the program's code</param>
        private void ParseSourceCode(string SourceCode)
        {
            string[] stringLines = SourceCode.Split(new char[] { '\n', '\r' });
            TreeNode parentNode = mainNode;

            for (int idx = 0; idx < stringLines.Length; idx++)
            {
                List<CodeElement> lineElements = GetLineElements(stringLines[idx], idx);

                foreach (CodeElement codeElement in lineElements)
                {
                    TreeNode newNode = null;

                    if (codeElement.Type == CodeElementType.process)
                    {
                        if (newNode == null)
                        {
                            newNode = codeTree.Nodes.Add(codeElement.Name);
                        }

                        parentNode = newNode;
                    }
                    else
                    {
                        newNode = parentNode.Nodes.Add(codeElement.Name);
                    }

                    if (newNode != null)
                    {
                        newNode.ImageIndex = (int)codeElement.Type;
                        newNode.SelectedImageIndex = (int)codeElement.Type;
                        newNode.ForeColor = codeElement.ElementColor;
                        newNode.Tag = codeElement;
                    }
                }
            }

            codeTree.Invalidate();
        }
        #endregion

        #region Form event handlers
        /// <summary>
        ///     Process tool-bar associated functions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolButtonPress(object sender, EventArgs e)
        {
            if (sender == tsbRefresh)
            {
                ReloadProject(project);
            }
            else if (sender == tsbExpandAll)
            {
                codeTree.ExpandAll();
            }
            else  if (sender == tsbColapseAll)
            {
                codeTree.CollapseAll();
            }
        }

        /// <summary>
        ///     Handle on tree list double click (seek to code element in
        /// the source file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void codeTree_DoubleClick(object sender, EventArgs e)
        {
            TreeNode selectedNode = codeTree.SelectedNode;
            if (ExploreCode != null && selectedNode != null && codeTree.SelectedNode.Tag != null)
            {
                CodeElement codeElement = (CodeElement)selectedNode.Tag;
                ExploreCode(sender, project, codeElement.SourceLine, 0, codeElement.LineText.Length);
            }
        }

        #endregion

        #region Explore code element delegate (seek to code element)
        public delegate void ExploreCodeElementDelegate(object sender, ProjectDescriptor projectDescriptor, int lineNumber, int offset, int length);
        public event ExploreCodeElementDelegate ExploreCode;
        #endregion
    
        #region Public properties
        /// <summary>
        ///     Associated project descriptor
        /// </summary>
        public ProjectDescriptor Project 
        {
            get { return project; }
            set { project = value; }
        }
        #endregion
    }
}

